#include "stdafx.h"
#include "stdlib/timezone.hpp"
#include "platform.hpp"
#include "stdlib/exception.hpp"
using namespace JHCPP::stdlib::jhException;
#include <ctime>


#if defined(JH_OS_WINDOWS)
#include <Windows.h>
#elif defined(JH_OS_LINUX)
#include "event/mutex.hpp"
using namespace JHCPP::event;
#include "event/scopedlock.hpp"

class TZInfo
{
public:
	TZInfo()
	{
		tzset();
	}

	int timeZone()
	{
		CMutex::ScopedLock lock(m_mutex);
		tzset();
		return -timezone;
	}

	const char* name(bool dst)
	{
		CMutex::ScopedLock lock(m_mutex);
		tzset();		
		return tzname[dst ? 1 : 0];
	}

private:
	CMutex m_mutex;
};

static TZInfo tzInfo;

#endif

namespace JHCPP
{
	namespace stdlib
	{
		int CTimeZone::utcOffset()
		{
		#if defined(JH_OS_WINDOWS)
			TIME_ZONE_INFORMATION tzInfo;
			DWORD dstFlag = ::GetTimeZoneInformation(&tzInfo);
			return -tzInfo.Bias*60;
		#elif defined(JH_OS_LINUX)
			return tzInfo.timeZone();
		#endif
		}

		int CTimeZone::dst()
		{
		#if defined(JH_OS_WINDOWS)
			TIME_ZONE_INFORMATION tzInfo;
			DWORD dstFlag = ::GetTimeZoneInformation(&tzInfo);
			return dstFlag == TIME_ZONE_ID_DAYLIGHT ? -tzInfo.DaylightBias*60 : 0;
		#elif defined(JH_OS_LINUX)
			std::time_t now = std::time(NULL);
			struct std::tm t;
			if (!localtime_r(&now, &t))
				throw SystemException("cannot get local time DST offset");
			return t.tm_isdst == 1 ? 3600 : 0;
		#endif
		}

		bool CTimeZone::isDst(const CTimeStamp& timestamp)
		{
			std::time_t time = timestamp.epochTime();
			struct std::tm* tms = std::localtime(&time);
			if (!tms) throw SystemException("cannot get local time DST flag");
			return tms->tm_isdst > 0;
		}

		int CTimeZone::tzd()
		{
			return utcOffset() + dst();
		}

		std::string CTimeZone::name()
		{
		#if defined(JH_OS_WINDOWS)
			std::string result;
			TIME_ZONE_INFORMATION tzInfo;
			DWORD dstFlag = ::GetTimeZoneInformation(&tzInfo);
			WCHAR* ptr = dstFlag == TIME_ZONE_ID_DAYLIGHT ? tzInfo.DaylightName : tzInfo.StandardName;

			char buffer[256];
			DWORD rc = ::WideCharToMultiByte(CP_ACP, 0, ptr, -1, buffer, sizeof(buffer), NULL, NULL);
			if (rc) 
				result = buffer;
			return result;
		#elif defined(JH_OS_LINUX)
			return std::string(tzInfo.name(dst() != 0));
		#endif
		}

		std::string CTimeZone::standardName()
		{
		#if defined(JH_OS_WINDOWS)
			std::string result;
			TIME_ZONE_INFORMATION tzInfo;
			DWORD dstFlag = ::GetTimeZoneInformation(&tzInfo);
			WCHAR* ptr = tzInfo.StandardName;

			char buffer[256];
			DWORD rc = ::WideCharToMultiByte(CP_ACP, 0, ptr, -1, buffer, sizeof(buffer), NULL, NULL);
			if (rc) 
				result = buffer;
			return result;
		#elif defined(JH_OS_LINUX)
			return std::string(tzInfo.name(false));
		#endif
		}

		std::string CTimeZone::dstName()
		{
		#if defined(JH_OS_WINDOWS)
			std::string result;
			TIME_ZONE_INFORMATION tzInfo;
			DWORD dstFlag = GetTimeZoneInformation(&tzInfo);
			WCHAR* ptr = tzInfo.DaylightName;

			char buffer[256];
			DWORD rc = WideCharToMultiByte(CP_ACP, 0, ptr, -1, buffer, sizeof(buffer), NULL, NULL);
			if (rc) 
				result = buffer;
			return result;
		#elif defined(JH_OS_LINUX)
			return std::string(tzInfo.name(true));
		#endif
		}

	}//end of namespace stdlib
}//end of namespace JHCPP
